//go:build darwin
// +build darwin

/*
目前mac的网卡管理，使用了系统自带的 networksetup 命令进行操作
系统是 14.1， 其他的版本不确定
*/

package network

import (
	"errors"
	"fmt"
	"go.uber.org/zap"
	"net"
	"onvif_server/pkg/log"
	"regexp"
	"strings"
)

func GetNetInterfaces() []NetInterface {
	res := make([]NetInterface, 0)
	hwMapDevice := getDeviceMapHw()
	var err error
	for dev, hw := range hwMapDevice {
		ifc := getNetworkByHw(hw)
		if ifc.IP != "" {
			ifc.Ifc, err = net.InterfaceByName(dev)
			if err != nil || ifc.Ifc == nil {
				continue
			}
			ifc.Name = dev
			res = append(res, ifc)
		}
	}
	return res
}

func SetNetInterface(ifc NetInterface) error {
	if ifc.Name == "" {
		return errors.New("网卡名称不能为空")
	}
	hwMapDevice := getDeviceMapHw()
	hwName, ok := hwMapDevice[ifc.Name]
	if !ok {
		return errors.New("未获取到网络设备:" + ifc.Name)
	}
	if ifc.Mode == DHCP {
		cmd := fmt.Sprintf("networksetup -setdhcp \"%s\"", hwName)
		output, err := runCmd(cmd)
		if err != nil {
			log.Log.Error("SetNetwork error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Object("ifc", ifc), zap.Error(err))
			return err
		}
	} else if ifc.Mode == STATIC {
		if ifc.IP == "" || ifc.Mask == "" || ifc.Gateway == "" {
			return errors.New("配置错误")
		}
		//设置网卡ip
		cmd := fmt.Sprintf("networksetup -setmanual \"%s\" %s %s %s", hwName, ifc.IP, ifc.Mask, ifc.Gateway)
		output, err := runCmd(cmd)
		if err != nil {
			log.Log.Error("SetNetwork error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Object("ifc", ifc), zap.Error(err))
			return err
		}
		//设置dns
		if ifc.DNS != "" {
			cmd = fmt.Sprintf("networksetup -setdnsservers \"%s\" %s", hwName, ifc.DNS)
			output, err = runCmd(cmd)
			if err != nil {
				log.Log.Error("SetNetwork error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Object("ifc", ifc), zap.Error(err))
				return err
			}
		}
	}
	log.Log.Info("SetNetwork success", zap.Object("ifc", ifc))
	return nil
}

/**
cmd:networksetup -listallhardwareports
output:
Hardware Port: Ethernet
Device: en0
Ethernet Address: 5c:1b:f4:e1:bf:ef

Hardware Port: Ethernet Adapter (en4)
Device: en4
Ethernet Address: e2:9f:a0:bd:af:d6

Hardware Port: Ethernet Adapter (en5)
Device: en5
Ethernet Address: e2:9f:a0:bd:af:d7

Hardware Port: Thunderbolt Bridge
Device: bridge0
Ethernet Address: 36:b3:bb:9d:92:40

Hardware Port: Wi-Fi
Device: en1
Ethernet Address: 5c:1b:f4:e2:0e:da

Hardware Port: iPhone USB
Device: en6
Ethernet Address: 92:8c:43:7d:0c:49

VLAN Configurations
===================
*/

func getHardwareByDevice(deviceName string) string {
	cmd := "networksetup -listallhardwareports"
	output, err := runCmd(cmd)
	if err != nil {
		log.Log.Error("getHardwareByDevice error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.String("deviceName", deviceName), zap.Error(err))
		return ""
	}
	arr := strings.Split(string(output), "\n")
	hwPre := "Hardware Port:"
	dnPre := "Device:"
	for i := 0; i < len(arr)-2; i++ {
		if strings.HasPrefix(arr[i], hwPre) && strings.HasPrefix(arr[i+1], dnPre) {
			if strings.TrimSpace(strings.TrimPrefix(arr[i+1], dnPre)) == deviceName {
				return strings.TrimSpace(strings.TrimPrefix(arr[i], hwPre))
			}
		}
	}
	return ""
}

/**
cmd:networksetup -listnetworkserviceorder
output:
An asterisk (*) denotes that a network service is disabled.
(1) Ethernet
(Hardware Port: Ethernet, Device: en0)

(2) Thunderbolt Bridge
(Hardware Port: Thunderbolt Bridge, Device: bridge0)

(3) Wi-Fi
(Hardware Port: Wi-Fi, Device: en1)

(4) iPhone USB USB
(Hardware Port: iPhone USB, Device: en6)
*/

func getDeviceMapHw() map[string]string {
	res := make(map[string]string)
	cmd := "networksetup -listnetworkserviceorder"
	output, err := runCmd(cmd)
	if err != nil {
		log.Log.Error("getHardwareByDevice error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Error(err))
		return res
	}
	arr := strings.Split(string(output), "\n")
	hwReg := regexp.MustCompile(`^\(\d+\) (.+)`)
	deviceReg := regexp.MustCompile(`^\(Hardware Port: [^,]+, Device: (.+)\)`)
	i := 0
	l := len(arr) - 1
	for {
		if i >= l {
			break
		}
		hws := hwReg.FindAllStringSubmatch(arr[i], -1)
		if len(hws) > 0 {
			ds := deviceReg.FindAllStringSubmatch(arr[i+1], -1)
			if len(ds) > 0 {
				res[ds[0][1]] = hws[0][1]
				i++
			}
		}
		i++
	}
	return res
}

/**
cmd:networksetup -getinfo "iPhone USB USB"
output:
DHCP Configuration
Client ID:
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Ethernet Address: 92:8c:43:7d:0c:49

cmd:networksetup -getinfo "Wi-Fi"
output:
DHCP Configuration
IP address: 192.168.2.189
Subnet mask: 255.255.255.0
Router: 192.168.2.1
Client ID:
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Wi-Fi ID: 5c:1b:f4:e2:0e:da

cmd:networksetup -getinfo "Ethernet"
output:
Manual Configuration
IP address: 192.168.2.55
Subnet mask: 255.255.255.0
Router: 192.168.2.1
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Ethernet Address: 5c:1b:f4:e1:bf:ef
*/

func getNetworkByHw(hwName string) NetInterface {
	res := NetInterface{Mode: STATIC}
	cmd := fmt.Sprintf("networksetup -getinfo \"%s\"", hwName)
	output, err := runCmd(cmd)
	if err != nil {
		log.Log.Error("getNetworkByHw error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Error(err))
		return res
	}
	str := string(output)
	modeReg := regexp.MustCompile(`(\w+) Configuration`)
	ipReg := regexp.MustCompile(`IP address: ([0-9|.]+)`)
	maskReg := regexp.MustCompile(`Subnet mask: ([0-9|.]+)`)
	gwReg := regexp.MustCompile(`Router: ([0-9|.]+)`)

	//fmt.Println("modeReg", modeReg.FindAllStringSubmatch(str, -1))
	//fmt.Println("ipReg", ipReg.FindAllStringSubmatch(str, -1))
	//fmt.Println("maskReg", maskReg.FindAllStringSubmatch(str, -1))
	//fmt.Println("gwReg", gwReg.FindAllStringSubmatch(str, -1))

	arr := modeReg.FindAllStringSubmatch(str, -1)
	if len(arr) > 0 && strings.ToLower(arr[0][1]) == DHCP {
		res.Mode = DHCP
	}

	arr = ipReg.FindAllStringSubmatch(str, -1)
	if len(arr) > 0 {
		res.IP = arr[0][1]
	}
	arr = maskReg.FindAllStringSubmatch(str, -1)
	if len(arr) > 0 {
		res.Mask = arr[0][1]
	}
	arr = gwReg.FindAllStringSubmatch(str, -1)

	if len(arr) > 0 {
		res.Gateway = arr[0][1]
	}

	if res.IP != "" {
		res.DNS = getDnsByHw(hwName)
	}

	return res
}

/**
cmd:networksetup -getdnsservers "Wi-Fi"
output:
8.8.8.8
4.4.4.4
*/

func getDnsByHw(hwName string) string {
	cmd := fmt.Sprintf("networksetup -getdnsservers \"%s\"", hwName)
	output, err := runCmd(cmd)
	if err != nil {
		log.Log.Error("getDnsByHw error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Error(err))
		return ""
	}
	ipReg := regexp.MustCompile(`([0-9|.]+)`)
	arr := ipReg.FindAllStringSubmatch(string(output), -1)
	//直接取第一个匹配的dns
	if len(arr) > 0 {
		return arr[0][1]
	}
	return ""
}
